/*
===========================================================================
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// cg_attachment.c -- an abstract attachment system

#include "cg_local.h"

/*
===============
CG_AttachmentPoint

Return the attachment point
===============
*/
qboolean CG_AttachmentPoint(attachment_t * a, vec3_t v)
{
	centity_t      *cent;

	if(!a)
		return qfalse;

	// if it all breaks, then use the last point we know was correct
	VectorCopy(a->lastValidAttachmentPoint, v);

	switch (a->type)
	{
		case AT_STATIC:
			if(!a->staticValid)
				return qfalse;

			VectorCopy(a->origin, v);
			break;

		case AT_TAG:
			if(!a->tagValid)
				return qfalse;

			AxisCopy(axisDefault, a->re.axis);
			CG_PositionRotatedEntityOnTag(&a->re, &a->parent, a->model, a->tagName);
			VectorCopy(a->re.origin, v);
			break;

		case AT_CENT:
			if(!a->centValid)
				return qfalse;

			if(a->centNum == cg.predictedPlayerState.clientNum)
			{
				// this is smoother if it's the local client
				VectorCopy(cg.predictedPlayerState.origin, v);
			}
			else
			{
				cent = &cg_entities[a->centNum];
				VectorCopy(cent->lerpOrigin, v);
			}
			break;

		case AT_PARTICLE:
			if(!a->particleValid)
				return qfalse;

			if(!a->particle->valid)
			{
				a->particleValid = qfalse;
				return qfalse;
			}
			else
				VectorCopy(a->particle->origin, v);
			break;

		default:
			CG_Printf(S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n");
			break;
	}

	if(a->hasOffset)
		VectorAdd(v, a->offset, v);

	VectorCopy(v, a->lastValidAttachmentPoint);

	return qtrue;
}

/*
===============
CG_AttachmentDir

Return the attachment direction
===============
*/
qboolean CG_AttachmentDir(attachment_t * a, vec3_t v)
{
	vec3_t          forward;
	centity_t      *cent;

	if(!a)
		return qfalse;

	switch (a->type)
	{
		case AT_STATIC:
			return qfalse;
			break;

		case AT_TAG:
			if(!a->tagValid)
				return qfalse;

			VectorCopy(a->re.axis[0], v);
			break;

		case AT_CENT:
			if(!a->centValid)
				return qfalse;

			cent = &cg_entities[a->centNum];
			AngleVectors(cent->lerpAngles, forward, NULL, NULL);
			VectorCopy(forward, v);
			break;

		case AT_PARTICLE:
			if(!a->particleValid)
				return qfalse;

			if(!a->particle->valid)
			{
				a->particleValid = qfalse;
				return qfalse;
			}
			else
				VectorCopy(a->particle->velocity, v);
			break;

		default:
			CG_Printf(S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n");
			break;
	}

	VectorNormalize(v);
	return qtrue;
}

/*
===============
CG_AttachmentAxis

Return the attachment axis
===============
*/
qboolean CG_AttachmentAxis(attachment_t * a, vec3_t axis[3])
{
	centity_t      *cent;

	if(!a)
		return qfalse;

	switch (a->type)
	{
		case AT_STATIC:
			return qfalse;
			break;

		case AT_TAG:
			if(!a->tagValid)
				return qfalse;

			AxisCopy(a->re.axis, axis);
			break;

		case AT_CENT:
			if(!a->centValid)
				return qfalse;

			cent = &cg_entities[a->centNum];
			AnglesToAxis(cent->lerpAngles, axis);
			break;

		case AT_PARTICLE:
			return qfalse;
			break;

		default:
			CG_Printf(S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n");
			break;
	}

	return qtrue;
}

/*
===============
CG_AttachmentVelocity

If the attachment can have velocity, return it
===============
*/
qboolean CG_AttachmentVelocity(attachment_t * a, vec3_t v)
{
	if(!a)
		return qfalse;

	if(a->particleValid && a->particle->valid)
	{
		VectorCopy(a->particle->velocity, v);
		return qtrue;
	}
	else if(a->centValid)
	{
		centity_t      *cent = &cg_entities[a->centNum];

		VectorCopy(cent->currentState.pos.trDelta, v);
		return qtrue;
	}

	return qfalse;
}

/*
===============
CG_AttachmentCentNum

If the attachment has a centNum, return it
===============
*/
int CG_AttachmentCentNum(attachment_t * a)
{
	if(!a || !a->centValid)
		return -1;

	return a->centNum;
}

/*
===============
CG_Attached

If the attachment is valid, return qtrue
===============
*/
qboolean CG_Attached(attachment_t * a)
{
	if(!a)
		return qfalse;

	return a->attached;
}

/*
===============
CG_AttachToPoint

Attach to a point in space
===============
*/
void CG_AttachToPoint(attachment_t * a)
{
	if(!a || !a->staticValid)
		return;

	a->type = AT_STATIC;
	a->attached = qtrue;
}

/*
===============
CG_AttachToCent

Attach to a centity_t
===============
*/
void CG_AttachToCent(attachment_t * a)
{
	if(!a || !a->centValid)
		return;

	a->type = AT_CENT;
	a->attached = qtrue;
}

/*
===============
CG_AttachToTag

Attach to a model tag
===============
*/
void CG_AttachToTag(attachment_t * a)
{
	if(!a || !a->tagValid)
		return;

	a->type = AT_TAG;
	a->attached = qtrue;
}

/*
===============
CG_AttachToParticle

Attach to a particle
===============
*/
void CG_AttachToParticle(attachment_t * a)
{
	if(!a || !a->particleValid)
		return;

	a->type = AT_PARTICLE;
	a->attached = qtrue;
}

/*
===============
CG_SetAttachmentPoint
===============
*/
void CG_SetAttachmentPoint(attachment_t * a, vec3_t v)
{
	if(!a)
		return;

	VectorCopy(v, a->origin);
	a->staticValid = qtrue;
}

/*
===============
CG_SetAttachmentCent
===============
*/
void CG_SetAttachmentCent(attachment_t * a, centity_t * cent)
{
	if(!a || !cent)
		return;

	a->centNum = cent->currentState.number;
	a->centValid = qtrue;
}

/*
===============
CG_SetAttachmentTag
===============
*/
void CG_SetAttachmentTag(attachment_t * a, refEntity_t parent, qhandle_t model, char *tagName)
{
	if(!a)
		return;

	a->parent = parent;
	a->model = model;
	strncpy(a->tagName, tagName, MAX_STRING_CHARS);
	a->tagValid = qtrue;
}

/*
===============
CG_SetAttachmentParticle
===============
*/
void CG_SetAttachmentParticle(attachment_t * a, particle_t * p)
{
	if(!a)
		return;

	a->particle = p;
	a->particleValid = qtrue;
}

/*
===============
CG_SetAttachmentOffset
===============
*/
void CG_SetAttachmentOffset(attachment_t * a, vec3_t v)
{
	if(!a)
		return;

	VectorCopy(v, a->offset);
	a->hasOffset = qtrue;
}
